﻿using Acl.Messages;
using Acl.Messages.Impl;

using NATS.Client.Hosting;

namespace Microsoft.Extensions.DependencyInjection;

/// <summary>
/// 分布式消息总线扩展
/// </summary>
public static class DistributionMessageBusExtensions
{
    /// <summary>
    /// 注册分布式消息总线
    /// </summary>
    /// <param name="services"></param>
    /// <param name="poolSize"></param>
    /// <param name="url"></param>
    /// <returns></returns>
    /// <exception cref="Exception"></exception>
    /// <exception cref="ArgumentException"></exception>
    public static IServiceCollection AddDistributionMessageBus(this IServiceCollection services, uint poolSize, string? url, Func<DistributionMessageBusOptions, DistributionMessageBusOptions>? configOptions)
    {
        var options = DistributionMessageBusOptions.Default;
        if (configOptions != null)
        {
            options = configOptions(options);
        }

        if (!string.IsNullOrWhiteSpace(url))
        {
            options = options with { Url = url };
        }


      

        CheckUrls(options.Url.Split(','));

        static void CheckUrls(params string[] urls)
        {
            foreach (var urlString in urls)
            {
                if (!urlString.Contains("://")) continue;

                var uriBuilder = new UriBuilder(new Uri(urlString, UriKind.Absolute));
                if (string.IsNullOrEmpty(uriBuilder.Host))
                {
                    uriBuilder.Host = "localhost";
                }

                switch (uriBuilder.Scheme)
                {
                    case "nats":
                    case "ws":
                    case "wss":
                        continue;
                    default:
                        throw new ArgumentException($"不支持 scheme {uriBuilder.Scheme} in nats URL {urlString}", urlString);
                }
            }
        }

        //确保自动开启连接池
        if (poolSize == 0) poolSize = 1u;

     

        services.AddNats((int)poolSize, opt =>
        {
            var ret = opt with
            {
                Url = options.Url,
                Name = options.Name,
                Echo = options.Echo,
                Verbose = options.Verbose,
                Headers = options.Headers,
                WriterBufferSize = options.WriterBufferSize,
                ReaderBufferSize = options.ReaderBufferSize,
                UseThreadPoolCallback = options.UseThreadPoolCallback,
                InboxPrefix = options.InboxPrefix,
                NoRandomize = options.NoRandomize,
                PingInterval = options.PingInterval,
                MaxPingOut = options.MaxPingOut,
                ReconnectWaitMax = options.ReconnectWaitMax,
                ReconnectWaitMin = options.ReconnectWaitMin,
                ReconnectJitter = options.ReconnectJitter,
                ConnectTimeout = options.ConnectTimeout,
                ObjectPoolSize = options.ObjectPoolSize,
                RequestTimeout = options.RequestTimeout,
                CommandTimeout = options.CommandTimeout,
                SubscriptionCleanUpInterval = options.SubscriptionCleanUpInterval,
                WriterCommandBufferLimit = options.WriterCommandBufferLimit,
                HeaderEncoding = options.HeaderEncoding,
            };

            if (!options.AuthOpts.IsAnonymous)
            {
                ret = ret with
                {
                    AuthOpts = ret.AuthOpts with
                    {
                        CredsFile = options.AuthOpts.CredsFile,
                        Jwt = options.AuthOpts.Jwt,
                        NKey = options.AuthOpts.NKey,
                        NKeyFile = options.AuthOpts.NKeyFile,
                        Password = options.AuthOpts.Password,
                        Seed = options.AuthOpts.Seed,
                        Token = options.AuthOpts.Token,
                        Username = options.AuthOpts.Username,
                    }
                };
            }

            return ret;
        });

        services.AddSingleton(options);
        services.AddSingleton<Func<NatsConnection>>(sp => () => sp.GetRequiredService<NatsConnection>());
        services.AddSingleton<IDistributionMessageBus, DistributionMessageBus>(); // 注册分布式消息总线

        services.AddSingleton(sp => (sp.GetRequiredService<IDistributionMessageBus>() as IDistributionMessageSubscriber)!);
        services.AddSingleton(sp => (sp.GetRequiredService<IDistributionMessageBus>() as IDistributionMessagePublisher)!);
        services.AddSingleton(sp => (sp.GetRequiredService<IDistributionMessageBus>() as IDistributionSender)!);
        services.AddSingleton(sp => sp.GetRequiredService<IDistributionMessageBus>().MessageHandleManagement);
        services.AddSingleton<IDistributionConnectionMonitor, DistributionConnectionMonitor>();

        return services;
    }
    /// <summary>
    /// 注册分布式消息总线
    /// </summary>
    /// <param name="services"></param>
    /// <param name="poolSize"></param>
    /// <param name="url"></param>
    /// <returns></returns>
    /// <exception cref="Exception"></exception>
    /// <exception cref="ArgumentException"></exception>
    public static IServiceCollection AddDistributionMessageBus(this IServiceCollection services, uint poolSize, string? url = "nats://localhost:4222") => AddDistributionMessageBus(services, poolSize, url,default);
}
